/**
 * 
 */

"use strict";

var collider = collider || {};

collider.collider = [];

/*
 * call for update
 */
collider.animLastUpdate = 0;
collider.pause = false;

collider.animUpdate = function(timeStamp) {

	var timeSpan = timeStamp - collider.animLastUpdate;

	if (collider.animLastUpdate && !collider.pause) {

		if (timeSpan > 2000) {
			// time out
			console
					.log('collider.animUpdate() --> animation watchdog, skip animation step with a time span of '
							+ timeSpan + 'ms');
		} else if (timeSpan >= 20) {
			// each 20 ms
			var instances = [];
			collider.collider.foreach(function(i, value, first, last) {
				instances.push(value);
			});
			// check each other
			while (instances.length > 1) {
				var current = instances.pop(instances.length - 1);
				instances.foreach(function(i, value, first, last) {
					// check if colliding happened
					current.isColliding(value);
				});
			}
		} else
			return;
	}
	// remind time stamp
	collider.animLastUpdate = timeStamp;
};

collider.restore = function(backup_values) {
	var colli = undefined;
	if (backup_values) {
		var track_field = fields.by_id[backup_values.track_field];
		if (track_field) {
			colli = new collider.Collider(track_field, 0, 0, 0, 0,
					backup_values.debug);
			var polygon = new utils.Polygon(new utils.Vec2(
					backup_values.polygonAx, backup_values.polygonAy),
					new utils.Vec2(backup_values.polygonBx,
							backup_values.polygonBy), new utils.Vec2(
							backup_values.polygonCx, backup_values.polygonCy),
					new utils.Vec2(backup_values.polygonDx,
							backup_values.polygonDy));
			if (colli) {
				colli.setColliderArea(polygon);
			}
		}
	}
	return colli;
};

collider.Collider = function(track_field, fwd_dist, bwd_dist, left_dist,
		right_dist, debug) {
	if (!track_field || !(track_field instanceof fields.Field))
		return;
	// remind base field
	this.track_field = track_field;
	this.track_field.collider = this;
	// calculate outer points
	var polygon = new utils.Polygon(new utils.Vec2(-left_dist, -fwd_dist),
			new utils.Vec2(right_dist, -fwd_dist), new utils.Vec2(right_dist,
					bwd_dist), new utils.Vec2(-left_dist, bwd_dist));
	this.setColliderArea(polygon);
	this.debug = debug;
	if (debug) {
		this.el = $('<div/>');
		$(traintest.ground).append(this.el);
	}
	// add to global list
	collider.collider.push(this);
};
collider.Collider.prototype.setColliderArea = function(polygon) {
	// precalculate square
	this.polygon = polygon;
	this.square = this.polygon.square();
	// precalculate max length from center pointer
	var maxDist = 0;
	var track_field = this.track_field;
	this.polygon.foreach(function(i, value, first, last) {
		var dist = track_field.vec2.distanceTo(value);
		maxDist = dist > maxDist ? dist : maxDist;
	});
	this.maxDist = maxDist;
};
collider.Collider.prototype.backup = function() {
	return {
		track_field : this.track_field.id,
		debug : this.debug,
		polygonAx : this.polygon[0].x,
		polygonAy : this.polygon[0].y,
		polygonBx : this.polygon[1].x,
		polygonBy : this.polygon[1].y,
		polygonCx : this.polygon[2].x,
		polygonCy : this.polygon[2].y,
		polygonDx : this.polygon[3].x,
		polygonDy : this.polygon[3].y
	};
};
collider.Collider.prototype.isColliding = function(other) {
	if (!this.track_field.collider || !this.track_field.placed()) {
		this.remove();
	} else if (other instanceof collider.Collider) {
		// first, check if distance between both objects less then maximum
		// distances
		var thisOffsVec2 = new utils.Vec2(this.track_field.column,
				this.track_field.row);
		var otherOffsVec2 = new utils.Vec2(other.track_field.column,
				other.track_field.row);
		var thisAbsVec2 = thisOffsVec2.add(this.track_field.vec2);
		var otherAbsVec2 = otherOffsVec2.add(other.track_field.vec2);
		var dist = thisAbsVec2.distanceTo(otherAbsVec2);
		if (!this.debug && dist >= (other.maxDist + this.maxDist))
			return false;
		// next step, check if points are inside each other
		if (this.debug) {
			// determine the left/top and right/bottom points when rotated
			var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE;
			this.polygon.foreach(function(i, value, first, last) {
				minX = value.x < minX ? value.x : minX;
				minY = value.y < minY ? value.y : minY;
				maxX = value.x > maxX ? value.x : maxX;
				maxY = value.y > maxY ? value.y : maxY;
			});
			var offsVec2 = new utils.Vec2(minX, minY);
			// calculate absolute position
			var topLeft = this.track_field.getCenterPos(offsVec2);

			// update style
			$(this.el).css(
					{
						width : Math.floor(this.polygon[0]
								.distanceTo(this.polygon[1])
								* defs.FIELD_WIDTH)
								+ 'px',
						height : Math.floor(this.polygon[1]
								.distanceTo(this.polygon[2])
								* defs.FIELD_HEIGHT)
								+ 'px',
						top : topLeft.top + 'px',
						left : topLeft.left + 'px',
						transform : 'rotate(' + this.track_field.vec2.a
								+ 'deg)',
						'z-index' : '99',
						position : 'absolute',
						border : 'solid thin red'
					});

			// determine the left/top and right/bottom points when rotated
			minX = Number.MAX_VALUE;
			minY = Number.MAX_VALUE;
			maxX = Number.MIN_VALUE;
			maxY = Number.MIN_VALUE;
			other.polygon.foreach(function(i, value, first, last) {
				minX = value.x < minX ? value.x : minX;
				minY = value.y < minY ? value.y : minY;
				maxX = value.x > maxX ? value.x : maxX;
				maxY = value.y > maxY ? value.y : maxY;
			});
			var offsVec2 = new utils.Vec2(minX, minY);
			// calculate absolute position
			var topLeft = other.track_field.getCenterPos(offsVec2);

			$(other.el).css(
					{
						width : Math.floor(other.polygon[0]
								.distanceTo(other.polygon[1])
								* defs.FIELD_WIDTH)
								+ 'px',
						height : Math.floor(other.polygon[1]
								.distanceTo(other.polygon[2])
								* defs.FIELD_HEIGHT)
								+ 'px',
						top : topLeft.top + 'px',
						left : topLeft.left + 'px',
						transform : 'rotate(' + other.track_field.vec2.a
								+ 'deg)',
						'z-index' : '99',
						position : 'absolute',
						border : 'solid thin red'
					});

		}
		
		var thisPoly = this.polygon.translate(this.track_field.vec2.a,
				thisOffsVec2);
		var otherPoly = other.polygon.translate(other.track_field.vec2.a,
				otherOffsVec2);
		var detected = false;

		// next step, check for cross points
		if (!detected)
			for (var ti = 0; ti < thisPoly.length - 1; ++ti) {
				var thisLine = new utils.Line(thisPoly[ti], thisPoly[ti + 1]);
				for (var oi = 0; oi < otherPoly.length - 1; ++oi) {
					var otherLine = new utils.Line(otherPoly[oi],
							otherPoly[oi + 1]);
					if (thisLine.crossAt(otherLine) !== undefined) {
						detected = true;
						break;
					}
				}
				if (detected)
					break;
			}

		if (detected) {
			// calculate collision velocity
			var thisRad = Math.rad(this.track_field.vec2.a);
			var thisVelo = this.track_field.speed || 0;
			var otherRad = Math.rad(other.track_field.vec2.a);
			var otherVelo = other.track_field.speed || 0;

			var thisVeloVec2 = new utils.Vec2(Math.sin(thisRad) * thisVelo,
					Math.cos(thisRad) * thisVelo);
			var otherVeloVec2 = new utils.Vec2(Math.sin(otherRad) * otherVelo,
					Math.cos(otherRad) * otherVelo);

			// fire event
			$(traintest.ground).trigger(
					'collision',
					[ this.track_field, other.track_field, dist,
							thisVeloVec2.sub(otherVeloVec2).length() ]);
			return true;
		}
	}
	return false;
};
collider.Collider.prototype.remove = function() {
	if (this.el)
		this.el.remove();
	collider.collider.remove(this);
};
